package com.basker.pisces.orm.data.meta;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.util.Assert;

import com.basker.pisces.core.meta.IDataField;

/**
 * 数据表的元信息，描述一个数据表的结构
 *
 * @author hangwen
 */
public class TableMeta {
    // 数据表名
    private String name;
    // 所有的字段map
    private Map<String, FieldMeta> fieldMetas;
    // 所有可以查询的字段
    private List<FieldMeta> selectFieldMetas;
    // 所有需要映射到对象的字段
    private List<FieldMeta> mapFieldMetas;
    // 所有需要插入的字段
    private List<FieldMeta> insertFieldMetas;
    // 所有需要更新的字段
    private List<FieldMeta> updateFieldMetas;
    // 主键字段
    private FieldMeta pkFieldMeta;
    // 关联父表的外键字段
    private FieldMeta fkFieldMeta;
    // 父表
    private TableMeta parentTableMeta;
    // 一对一扩展表 尚未实现
    private List<TableMeta> subTableMetas = new ArrayList<TableMeta>();
    // 所有子表
    private List<TableMeta> childTableMetas = new ArrayList<TableMeta>();
    // 索引
    private List<IndexMeta> indexMetas = new ArrayList<IndexMeta>();

    public TableMeta(String name) {
        this.name = name;
    }

    /**
     * 添加子表
     *
     * @param tableMeta
     */
    public void addChildTableMeta(TableMeta tableMeta) {
        tableMeta.setParentTableMeta(this);
        childTableMetas.add(tableMeta);
    }

    /**
     * 为当前表添加字段
     *
     * @param fieldMeta
     * @return
     */
    public boolean addFieldMeta(FieldMeta fieldMeta) {
        if (this.fieldMetas == null) {
            this.fieldMetas = new LinkedHashMap<>();
        }

        String fieldName = fieldMeta.getName();

        if (this.fieldMetas.containsKey(fieldName)) {
            return false;
        }

        this.fieldMetas.put(fieldName, fieldMeta);

        IDataField dataField = fieldMeta.getDataField();
        if (dataField.isPkField()) {
            Assert.isTrue(fieldMeta.getTableName().equals(this.name),
                    "pk field's host table must be equal to main table!");
            this.setPkFieldMeta(fieldMeta);
        }

        return true;
    }

    /**
     * 添加一个索引
     *
     * @param indexMeta
     */
    public void addIndexMeta(IndexMeta indexMeta) {
        this.indexMetas.add(indexMeta);
    }

    /**
     * 添加一对一扩展表
     *
     * @param tableMeta
     */
    public void addSubTableMeta(TableMeta tableMeta) {
        tableMeta.setParentTableMeta(this);
        subTableMetas.add(tableMeta);
    }

    /**
     * 获取所有子表
     *
     * @return
     */
    public List<TableMeta> getChildTableMetas() {
        return childTableMetas;
    }

    /**
     * 获取所有字段，按{@link FieldMeta#getDbColumnSort()排序}这是一个只读集合
     *
     * @return
     */
    public List<FieldMeta> getFieldMetas() {
        if (this.fieldMetas == null) {
            return Collections.emptyList();
        }

        return Collections.unmodifiableList(this.fieldMetas.values().stream().sorted((f1, f2) -> {
            int c = Integer.compare(f1.getSort(), f2.getSort());
            return c == 0 ? f1.getName().compareTo(f2.getName()) : c;
        }).collect(Collectors.toList()));

    }

    /**
     * 获取关联父表的外键字段
     *
     * @return
     */
    public FieldMeta getFkFieldMeta() {
        return fkFieldMeta;
    }

    public List<IndexMeta> getIndexMetas() {
        return indexMetas;
    }

    /**
     * 获取需要新增的字段，等同于所有字段
     *
     * @return
     */
    public List<FieldMeta> getInsertFields() {
        if (this.insertFieldMetas == null) {
            this.insertFieldMetas = new ArrayList<>(this.fieldMetas.values());
        }
        return this.insertFieldMetas;
    }

    /**
     * 获取需要映射到对象的字段，包括本表字段和其所有的一对一扩展表上的字段
     *
     * @return
     */
    public List<FieldMeta> getMapFieldMetas() {
        if (this.mapFieldMetas == null) {
            mapFieldMetas = new ArrayList<FieldMeta>();

            mapFieldMetas.addAll(this.getFieldMetas());
            for (TableMeta tableMeta : subTableMetas) {
                mapFieldMetas.addAll(tableMeta.getMapFieldMetas());
            }
        }

        return mapFieldMetas;
    }

    /**
     * 获取数据表的名称
     *
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * 获取父表
     *
     * @return
     */
    public TableMeta getParentTableMeta() {
        return parentTableMeta;
    }

    /**
     * 获取主键字段
     *
     * @return
     */
    public FieldMeta getPkFieldMeta() {
        return pkFieldMeta;
    }

    /**
     * 获取所有查询需要的字段，包括本表字段，所有一对一扩展表的字段，以及关联父表的外键（如果有的话）
     *
     * @return
     */
    public List<FieldMeta> getSelectFieldMetas() {
        if (this.selectFieldMetas == null) {
            // buildSelectFieldMetas,包括subtables的字段
            selectFieldMetas = new ArrayList<FieldMeta>();
            selectFieldMetas.addAll(this.getFieldMetas());

            for (TableMeta subTableMeta : subTableMetas) {
                selectFieldMetas.addAll(subTableMeta.getSelectFieldMetas());
            }

            if (this.isChildTable()) {
                selectFieldMetas.add(fkFieldMeta);
            }
        }

        return selectFieldMetas;
    }

    /**
     * 获取指定的一对一扩展表
     *
     * @param subtableName
     * @return
     */
    public TableMeta getSubTableMeta(String subtableName) {
        Optional<TableMeta> optional = this.subTableMetas.stream().filter(t -> t.getName().equals(subtableName))
                .findFirst();

        return optional.isPresent() ? optional.get() : null;
    }

    /**
     * 获取所有一对一扩展表
     *
     * @return
     */
    public List<TableMeta> getSubTableMetas() {
        return subTableMetas;
    }

    /**
     * 获取所有需要更新的字段，除去字段的所有本表字段
     *
     * @return
     */
    public List<FieldMeta> getUpdateFields() {
        if (this.updateFieldMetas == null) {
            updateFieldMetas = new ArrayList<FieldMeta>();

            for (FieldMeta fieldMeta : this.getFieldMetas()) {
                if (fieldMeta == pkFieldMeta || fieldMeta.getName().equals(pkFieldMeta.getName())) {
                    continue;
                }

                updateFieldMetas.add(fieldMeta);
            }

        }
        return updateFieldMetas;
    }

    public boolean isChildTable() {
        return this.fkFieldMeta != null && !this.fkFieldMeta.getName().equals(this.pkFieldMeta.getName());
    }

    public boolean isSubTable() {
        return this.fkFieldMeta != null && this.fkFieldMeta.getName().equals(this.pkFieldMeta.getName());
    }

    public void setFkFieldMeta(FieldMeta fkFieldMeta) {
        this.fkFieldMeta = fkFieldMeta;
    }

    public void setIndexMetas(List<IndexMeta> indexMetas) {
        this.indexMetas = indexMetas;
    }

    public void setParentTableMeta(TableMeta parentTableMeta) {
        this.parentTableMeta = parentTableMeta;
    }

    public void setPkFieldMeta(FieldMeta pkFieldMeta) {
        this.pkFieldMeta = pkFieldMeta;
    }

    @Override
    public String toString() {
        return this.name;
    }

}
